import openpyxl
import numpy as np
import math
from matplotlib import cm
import matplotlib.pyplot as plt

filename = 'Test_Train_Set.xlsx'
xlspath = filename
Data_All = []
test_Desired = []
test_Input1 = []
test_Input2 = []
train_Desired = []
train_Input1 = []
train_Input2 = []


# Logistic函数：lgstic‘(x) = lgstc(x)(1 - lgstc(x))
def lgstc(x):
    return 1 / (1 + np.exp(-x))


# w增量求解
def Del_w(type_, Input1, Input2, Desired, w0, w1, w2):
    sumDel = 0
    for i in range(len(Input1)):
        lc_p = lgstc(w0 + w1 * Input1[i] + w2 * Input2[i])
        if type_ == 0:
            sumDel += Desired[i] - lc_p
        elif type_ == 1:
            sumDel += Input1[i] * (Desired[i] - lc_p)
        elif type_ == 2:
            sumDel += Input2[i] * (Desired[i] - lc_p)
    return sumDel


# 损失函数loss迭代
def Loss(Input1, Input2, Desired, w0, w1, w2):
    loss = 0
    for i in range(len(Input1)):
        lc_p = lgstc(w0 + w1 * Input1[i] + w2 * Input2[i])
        loss += Desired[i] * np.log(lc_p) + (1 - Desired[i] )* np.log(1 - lc_p)
    return loss


# 梯度上升求极值
def GDM_LC(Input1, Input2, Desired, w0=0, w1=0, w2=0, Alpha=0.1, err=1e-9):
    w0_list = []
    w1_list = []
    w2_list = []
    loss_list = []
    IterTime = 0

    while True:
        w0, w1, w2 = \
            w0 + Alpha * Del_w(0, Input1, Input2, Desired, w0, w1, w2), \
            w1 + Alpha * Del_w(1, Input1, Input2, Desired, w0, w1, w2), \
            w2 + Alpha * Del_w(2, Input1, Input2, Desired, w0, w1, w2)
        w0_list.append(w0)
        w1_list.append(w1)
        w2_list.append(w2)
        loss_list.append(Loss(Input1, Input2, Desired, w0, w1, w2))
        IterTime += 1

        if IterTime > 3:
            if abs(w1_list[IterTime - 1] - w1_list[IterTime - 2]) < err and abs(
                    w2_list[IterTime - 1] - w2_list[IterTime - 2]) < err:
                break

    return w0, w1, w2, w0_list, w1_list, w2_list, loss_list, IterTime


# 测试集验证函数
def bool_rate(Input1, Input2, Desired, w0, w1, w2):
    all_num = len(Input1)
    t_num = 0
    for i in range(all_num):
        if (Desired[i] == 0) and (w0 + w1 * Input1[i] + w2 * Input2[i]) < 0:
            t_num += 1
        if (Desired[i] == 1) and (w0 + w1 * Input1[i] + w2 * Input2[i]) >= 0:
            t_num += 1
    return t_num / all_num


wb = openpyxl.load_workbook(xlspath, data_only=True)
sheet1 = wb.worksheets[0]
nrow = sheet1.max_row
ncols = sheet1.max_column

for c in range(1, ncols + 1):
    data1 = []
    for r in range(2, nrow + 1):
        data1.append(sheet1.cell(r, c).value)
    Data_All.append(list(data1))

test_Desired.append(Data_All[0])
test_Input1.append(Data_All[1])
test_Input2.append(Data_All[2])
train_Desired.append(Data_All[3])
train_Input1.append(Data_All[4])
train_Input2.append(Data_All[5])

test_DesiredM = np.array(test_Desired[0])
test_Input1M = np.array(test_Input1[0])
test_Input2M = np.array(test_Input2[0])
train_DesiredM = np.array(train_Desired[0])
train_Input1M = np.array(train_Input1[0])
train_Input2M = np.array(train_Input2[0])

Alpha = 0.1
err = 1e-9

w0, w1, w2, w0_list, w1_list, w2_list, loss_list, IterTime = \
    GDM_LC(test_Input1M, test_Input2M, test_DesiredM, 0, 0, 0, Alpha, err)
rate = bool_rate(test_Input1M, test_Input2M, test_DesiredM, w0, w1, w2)
rate2 = bool_rate(train_Input1M, train_Input2M, train_DesiredM, w0, w1, w2)
print(f'测试集样本数量：{len(test_DesiredM)}')
print(f'训练集样本数量：{len(test_DesiredM)}')
print(f'迭代次数:{IterTime}')
print(f'训练参数为  w0：{w0}  w1:{w1}  w2:{w2}')
print(test_Input1)
print(Del_w(1, test_Input1M, test_Input2M, test_DesiredM, 0, 0, 0))
print(f'测试集的正确率：{rate}')
print(f'训练集的正确率：{rate2}')

for i in range(len(loss_list)):
    if i % 100 == 0 :
        print(loss_list[i])
X = test_Input1M
Y = -w0 / w2 - w1 * X / w2
print(len(Y))
# 图像绘制
plt.figure('线性分类求解（数据集）')
plt.plot(X, Y)
for i in range(len(test_DesiredM)):
    if test_DesiredM[i] == 1:
        plt.scatter(test_Input1M[i], test_Input2M[i], c='r', marker='+')
    else:
        plt.scatter(test_Input1M[i], test_Input2M[i], c='b', marker='.')
plt.show()